package tij4.concurrent.exercise.ex35;

import com.sun.jmx.remote.internal.ArrayQueue;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.PriorityQueue;
import java.util.Random;
import java.util.concurrent.*;

/*
    客户端:
        需要服务器时间,在初始化时赋值,get方法返回
        需要服务器时间不需要改变,此类为只读对象
 */
class WebClient {
    private final int serviceTime;  //一点赋值就不能变化

    public WebClient(int serviceTime) {
        this.serviceTime = serviceTime;
    }

    public int getServiceTime() {
        return serviceTime;
    }

    @Override
    public String toString() {
        return "[ " + serviceTime + " ]";
    }
}

/*
    服务队列,客户端要在这里排队,客户可能是无尽的
        客户,规定最多的客户端
 */
class WebClientLine extends LinkedBlockingQueue<WebClient> {
    @Override
    public String toString() {
        if (this.size() == 0) {
            return "[Empty]";
        }else {
            StringBuilder result = new StringBuilder();
            Object[] objects = toArray();
            for (Object o : objects) {
                result.append(o.toString());
            }
            return result.toString();
        }
    }
}

/*
    客户端访问服务器端,将客户加入到队列中,相对于客户来说都是访问的一个服务器
    这个类在做事情
 */
class WebClientGenerator implements Runnable{
    private static Random random = new Random(47);
    private WebClientLine webClientLine;

    public WebClientGenerator(WebClientLine webClientLine) {
        this.webClientLine = webClientLine;
    }

    @Override
    public void run() {
        try {
            while (!Thread.interrupted()) {
                TimeUnit.MILLISECONDS.sleep(random.nextInt(200));
                webClientLine.put(new WebClient(random.nextInt(1000)));
            }
        }catch (InterruptedException e) {
            System.out.println("WebClientGenerator interrupted");
        }
    }
}

/*
    服务器给定数量
        客户端服务客户是数量是一定的
        服务客户,休眠
        需要办事情,需要被调度

        来个客户
        为客户服务
        服务过的客户数量 +1
        此时如果安排做其他任务就停止服务,线程进入 wait()状态
 */
class WebServer implements Runnable,Comparable<WebServer> {
    private static int counter = 0;
    private final int id = counter ++;
    private static WebClientLine webClientLine; //要接受调度给的队列
    private boolean servered = true;
    private int serveredCount = 0;

    public WebServer(WebClientLine webClientLine) {
        this.webClientLine = webClientLine;
    }
    public synchronized void doServer() {   //服务客户
        servered = true;
        notifyAll();
    }
    public synchronized void toSleep() {
        servered = false;
    }

    public int getServeredCount() {
        return serveredCount;
    }

    @Override
    public void run() {
        try {
            while (!Thread.interrupted()) {
                    WebClient webClient = webClientLine.take();
                    TimeUnit.MILLISECONDS.sleep(webClient.getServiceTime());
                    synchronized (this) {
                        serveredCount++;
                        while (!servered) {
                            wait();
                        }
                    }
                    toString();
            }
        }catch (InterruptedException e) {
            System.out.println("WebServer Interrupted");
        }
    }
    @Override
    public String toString() {
        return "WebServer" + id;
    }
    @Override
    public synchronized int compareTo(WebServer o) {
        return this.serveredCount < o.serveredCount ? -1 :
                (this.serveredCount == serveredCount ? 0 : 1);
    }
}

/*
    客户端访问服务器这件事情,调度中心

    调度,客户端是服务器的 [2,3) 倍数
        需要增多服务器的时候,当客户机为服务器的两倍以上时
            如果有休眠的服务器则唤醒使其工作
            如果没有则加入一台新的服务器
        需要减少服务器的时候,当客户机小于服务器的两倍的时候,但是要求服务器的属相大于1
            让一台服务器服务器去做其他的事情
          当客户为0的时候,留一台服务器候着就行

 */
class WebManager implements Runnable{
    private PriorityQueue<WebServer> workServers = new PriorityQueue<>();
    private LinkedList<WebServer> sleepServers = new LinkedList<>();
    private ExecutorService exec;
    private WebClientLine webClientLine;

    public WebManager(ExecutorService exec, WebClientLine webClientLine) {
        this.exec = exec;
        this.webClientLine = webClientLine;
        WebServer webServer = new WebServer(webClientLine);
        exec.execute(webServer);
        workServers.offer(webServer);
    }

    public void toSleep() {
        WebServer webServer = workServers.remove();
        webServer.toSleep();
        sleepServers.offer(webServer);
    }

    public synchronized void adjustWebServerNumber() {
        if (webClientLine.size()/workServers.size() > 2) {
            if (sleepServers.size() > 0) {
                WebServer webServer = sleepServers.remove();
                workServers.offer(webServer);
                return;
            }
            WebServer webServer = new WebServer(webClientLine);
            exec.execute(webServer);
            workServers.add(webServer);
        }
        System.out.println("------------------" + webClientLine.size() / workServers.size());
        if (workServers.size()>1 && webClientLine.size()/workServers.size() < 2) {
            WebServer webServer = workServers.remove();
            toSleep();
            sleepServers.offer(webServer);
        }
        if (sleepServers.size() == 0) {
            while (workServers.size() > 1) {
                toSleep();
            }
        }
    }

    /*
        过一段时间就根据客户端改变下服务器的服务器数量
     */
    @Override
    public void run() {
        try {
            while (!Thread.interrupted()) {
                TimeUnit.MILLISECONDS.sleep(1000);
                adjustWebServerNumber();
                System.out.println(webClientLine + "{");
                for (WebServer webServer : workServers) {
                    System.out.print(webServer + "  ");
                }
                System.out.println();
                System.out.println("}");
            }
        }catch (InterruptedException e) {
            System.out.println("WebManager interrupted");
        }
    }

}

public class WebServerSimulation {
    public static void main(String[] args) throws Exception{
        WebClientLine webClients = new WebClientLine();
        ExecutorService executorService = Executors.newCachedThreadPool();
        executorService.execute(new WebClientGenerator(webClients));
        executorService.execute(new WebManager(executorService, webClients));
        TimeUnit.SECONDS.sleep(5);
        executorService.shutdownNow();
    }
}
